/**
 * Copyright (C) 2017-2018 pinganfang, Inc. All Rights Reserved.
 */
package com.pingan.haofang.searchcloud.user.service.impl;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.pingan.haofang.searchcloud.common.constants.Status;
import com.pingan.haofang.searchcloud.common.exception.FieldException;
import com.pingan.haofang.searchcloud.mail.constants.MailConstants;
import com.pingan.haofang.searchcloud.mail.dto.MailDto;
import com.pingan.haofang.searchcloud.mail.service.MailService;
import com.pingan.haofang.searchcloud.user.dao.SearchProjectSpecs;
import com.pingan.haofang.searchcloud.user.dto.ProjectMailDto;
import com.pingan.haofang.searchcloud.user.dto.ProjectQueryDto;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.pingan.haofang.framework.common.functional.lang.control.bean.BeanCopy;
import com.pingan.haofang.searchcloud.user.constant.ProjectStatus;
import com.pingan.haofang.searchcloud.user.dao.SearchProjectDao;
import com.pingan.haofang.searchcloud.user.domain.SearchProject;
import com.pingan.haofang.searchcloud.user.dto.SearchProjectDto;
import com.pingan.haofang.searchcloud.user.service.SearchProjectService;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author huhailiang937
 * @date 2017年5月2日下午9:07:08
 * @since 1.0.0
 */
@Service
public class SearchProjectServiceImpl implements SearchProjectService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SearchProjectServiceImpl.class);

    @Autowired
    private SearchProjectDao searchProjectDao;

    @Autowired
    private MailService mailService;

    /**
     * 项目TOKEN Cache
     */
    private LoadingCache<Long, String> tokenCache = CacheBuilder.newBuilder().maximumSize(10000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<Long, String>() {
                @Override
                public String load(Long key) {
                    SearchProject searchProject = searchProjectDao.findOne(key);

                    if (searchProject == null
                            || searchProject.getStatus() != ProjectStatus.NORMAL.getId().intValue()) {
                        return StringUtils.EMPTY;
                    }
                    return searchProject.getToken();
                }
            });

    private LoadingCache<String, String> codeTokenCache = CacheBuilder.newBuilder().maximumSize(10000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, String>() {
                @Override
                public String load(String key) {
                    SearchProject searchProject = searchProjectDao.findByProjectCode(key);

                    if (searchProject == null
                            || searchProject.getStatus() != ProjectStatus.NORMAL.getId().intValue()) {
                        return StringUtils.EMPTY;
                    }
                    return searchProject.getToken();
                }
            });


    @Override
    public SearchProjectDto get(Long id) {
        SearchProject searchProject = searchProjectDao.findOne(id);
        if (searchProject == null) {
            return null;
        }

        return BeanCopy.toFunction(SearchProjectDto.class, BeanUtils::copyProperties).apply(searchProject);
    }

    @Override
    public String getTokenByProjectId(Long projectId) {
        try {
            return tokenCache.get(projectId);
        } catch (ExecutionException e) {
            LOGGER.info(e.getMessage(), e);
            return null;
        }
    }

    @Override
    @Transactional
    public List<SearchProjectDto> getList(ProjectQueryDto query) {
        List<SearchProject> searchProjectList =
                Optional.ofNullable(searchProjectDao.findAll(SearchProjectSpecs.listQuerySpec(query)))
                        .orElse(Collections.emptyList());

        return searchProjectList.stream()
                .map(BeanCopy.toFunction(SearchProjectDto.class, BeanUtils::copyProperties))
                .collect(Collectors.toList());
    }

    @Override
    public void delete(Long id) {
        SearchProject searchProject = searchProjectDao.getOne(id);
        searchProject.setStatus(ProjectStatus.DELETE.getId());
        searchProjectDao.save(searchProject);
    }

    @Override
    public Long update(SearchProjectDto dto) {

        Function<SearchProjectDto, SearchProject> transform = dtoTemp -> {
            SearchProject bo = new SearchProject();
            BeanCopy.of(dtoTemp, bo).copy(BeanUtils::copyProperties).get();

            return bo;
        };

        checkCodeOrNameExists(dto);

        SearchProject searchProject = transform.apply(dto);
        searchProject = searchProjectDao.save(searchProject);
        return searchProject.getId();
    }

    @Override
    public List<SearchProjectDto> findEnableProjects() {
        List<SearchProject> searchProjectList =
                Optional.ofNullable(searchProjectDao.findByStatus(ProjectStatus.NORMAL.getId()))
                        .orElse(Collections.emptyList());
        return searchProjectList.stream()
                .map(BeanCopy.toFunction(SearchProjectDto.class, BeanUtils::copyProperties))
                .collect(Collectors.toList());
    }

    @Override
    public String getTokenByProjectCode(String requestAppCode) {
        try {
            return codeTokenCache.get(requestAppCode);
        } catch (ExecutionException e) {
            LOGGER.info(e.getMessage(), e);
            return null;
        }
    }

    @Override
    public void sendTokenNotify(Long projectId, List<String> emailAddresses, String host) {
        MailDto<ProjectMailDto> mailDto = new MailDto<>();
        mailDto.setTemplateName(MailConstants.PROJECT_MAIL_TEMPLATE);
        mailDto.setEmailTo(emailAddresses);
        mailDto.setSubject("搜索云通知");

        ProjectMailDto projectMailDto = new ProjectMailDto();
        projectMailDto.setEnvironment(host);
        projectMailDto.setProject(get(projectId));
        mailDto.setModel(projectMailDto);
        mailService.sendMail(mailDto);
    }
    
    @Override
    public int countProject() {
        List<SearchProject> projects = searchProjectDao.findByStatus(Status.NORMAL.getValue());
        if (CollectionUtils.isNotEmpty(projects)) {
            return projects.size();
        }
        return 0;
    }

    /**
     * 监测是否重复
     *
     * @param dto
     */
    private void checkCodeOrNameExists(SearchProjectDto dto) {

        List<SearchProject> existProjects = searchProjectDao.findAll(
                SearchProjectSpecs.nameOrCodeMayExcludeIdSpec(
                        dto.getProjectName(), dto.getProjectCode(), dto.getId()));

        if (CollectionUtils.isNotEmpty(existProjects)) {
            throw new FieldException("project.duplicate");
        }
    }

}
